//===------------- SimplifyTypes.cpp - Simplify types in LLVM IR ----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass will simpfy types by removing unnecessary type hierarchy
//
//===----------------------------------------------------------------------===//
#include "reflow/LinkAllPasses.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/XILINXHLSValueTrackingUtils.h"
#include "llvm/IR/XILINXFPGAIntrinsicInst.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/NoFolder.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/Dominators.h"
#include "llvm/Transforms/Utils/Local.h"
#include "DisaggregateUtil.h"

#include <set>

#define DEBUG_TYPE "reflow-simplify-type"
using namespace llvm;

static cl::opt<bool>
    HandlePointerInStructOnly("pointer-in-struct-only", cl::init(false),
                              cl::Hidden,
                              cl::desc("handle pointer in struct."));

namespace {
struct SimplifyTypes : public ModulePass {
  static char ID; // Pass identification, replacement for typeid

  using  IndicesVector = SmallVector<unsigned,4> ; 

  const TargetLibraryInfo* TLI; 

  Module *CurM; 

  MapVector<Type*, Type*> structPeelMap; 
  //the struct cast function map , 
  MapVector<Type*, Function*> structCast1Map; 
  MapVector<Type*, Function*> structCast2Map; 

  std::list<WeakVH> bitcastDownWorkList; 
  std::list<WeakVH> bitcastUpWorkList; 
  std::list<WeakVH> cast1WorkList; 
  std::list<WeakVH> cast2WorkList; 

  SetVector<Function*> updateIntrinsicSet; 

  std::list<ExtractValueInst *> extractWorkList;  
  bool PointerInStructOnly ;

  SimplifyTypes(bool _PointerInStructOnly = false)
      : ModulePass(ID), PointerInStructOnly(_PointerInStructOnly) {
    if (HandlePointerInStructOnly)
      PointerInStructOnly = true;
    initializeSimplifyTypesPass(*PassRegistry::getPassRegistry());
  }

  void getAnalysisUsage(AnalysisUsage &Info) const override {
    Info.addRequired<TargetLibraryInfoWrapperPass>(); 
  }

  bool runOnModule(Module &M) override;

  Type* peelStructType(Type* type); 

  Constant* simplifyConstant(Constant *cv) ; 
  //TODO,  can we use  BitCast ?
  Value* castType(Value* SrcV, Type* dstType, IRBuilder<> &builder); 
  Value* simplifyGEP(GEPOperator* gep, Value* newBase) ;
  bool isCast1(Value* v); 
  bool isCast2(Value* v); 

  bool  isValidTypeCast(Type* srcType, Type* destType);

  bool populateBitCastDown(BitCastOperator* bitcast);
  bool populateBitCastUp(BitCastOperator* bitcast);

  bool populateCast1Up(CallInst* cast1);
  bool populateCast2Down(CallInst* cast2);
};


}

char SimplifyTypes::ID = 0;

bool  SimplifyTypes::isValidTypeCast(Type* srcType, Type* destType)
{
  Type * simplifiedSrcType = peelStructType(srcType); 
  Type * simplifiedDestType = peelStructType(destType); 

  if(srcType == simplifiedDestType || destType == simplifiedSrcType){ 
    return true; 
  }
  return false; 
}


/*
 * for following scenario: 
 * struct A0{ 
 *   struct B0* fieldA; 
 * }; 
 * struct B0{ 
 *    int fieldB; 
 * }
 *
 * void foo( struct A0 data) { 
 *    %v = extractValue(data, 0); 
 *    ...
 * }
 * ==============================> 
 * struct A0{ 
 *   struct B0* fieldA; 
 * }; 
 * struct A0.1{ 
 *    int *fieldA; 
 * }; 
 *
 * struct B0{ 
 *    int fieldB; 
 * }
 * void foo( int* data) { 
 *    %ret = cast2(data, struct A0); 
 *    %v = extractValue( %ret, 0); 
 *    ...
 * }
 *
 */


Value* SimplifyTypes::castType(Value *SrcV, Type* dstType, IRBuilder<> &builder) 
{
  Type* srcType = SrcV->getType(); 

  if (srcType == dstType) { 
    return SrcV; 
  }

  DEBUG(
      llvm::dbgs() << "cast from:\n"; 
      SrcV->dump(); 
      llvm::dbgs() << "to :\n"; 
      dstType->dump(); 
      ); 



  Type* retSrcType = peelStructType(srcType); 
  Type* retDstType = peelStructType(dstType);
  assert (retDstType == retSrcType && "unexpected"); 
  //handle some corner case : 
  if (isa<UndefValue>(SrcV)) { 
    Value *undef = UndefValue::get(dstType); 
    return undef; 
  }


  if (srcType->isPointerTy() && dstType->isPointerTy()) { 
    //handle the corner case :
    //if the "SrcV" is  ConstExpr, to avoid bitcast is fold
    //we need use NoFolder IRBuilder 
    Value *bitcast = bitcast = builder.CreateBitCast(SrcV, dstType); 

    if (retSrcType == dstType) { 
      bitcastUpWorkList.push_back(bitcast); 
    }
    else if (retDstType == srcType) { 
      bitcastDownWorkList.push_back(bitcast);
    }
    else { 
      llvm_unreachable( "unexpected, invalid castType call"); 
    }
    return bitcast; 
  }


  if (retDstType == srcType) { 
    assert(retDstType != dstType && "unexpected,  dstType should be Peeled type"); 

    Function * castFunc = structCast2Map[dstType]; 
    if (!castFunc) { 
      FunctionType *NewFuncTy = 
          FunctionType::get(dstType, true);
      
      castFunc = 
          Function::Create(NewFuncTy, GlobalValue::ExternalWeakLinkage, 
              "cast2", CurM);
      structCast2Map[dstType] = castFunc; 
    }

    //TODO, check cast2 + cast1 combine
    CallInst *call = builder.CreateCall(castFunc, makeArrayRef(SrcV)); 
    call->setDoesNotAccessMemory(); 
    cast2WorkList.push_back(call); 
    return call; 
  }
  else if (retSrcType == dstType){ 
    assert(retSrcType != srcType && "unexpected,  srcType should be Peeled type"); 
    Function * castFunc = structCast1Map[dstType]; 
    if (!castFunc) { 
      FunctionType *NewFuncTy = 
          FunctionType::get(dstType, true);
      
      castFunc = 
          Function::Create(NewFuncTy, GlobalValue::ExternalWeakLinkage, 
              "cast1", CurM);
      structCast1Map[dstType] = castFunc; 
    }
    //TODO, check cast2 + cast1 combine
    CallInst *call = builder.CreateCall(castFunc, makeArrayRef(SrcV)); 
    call->setDoesNotAccessMemory(); 
    cast1WorkList.push_back(call); 
    return call; 
  }

  llvm_unreachable("unexpected"); 
  return nullptr; 
}

ArrayRef<unsigned>  castIndices(ArrayRef<unsigned> indices, Type* origin, Type* ret)
{
  Type *field = origin; 
  unsigned i = 0; 
  while( isa<CompositeType>(field) && ret != field)  { 
    field = cast<CompositeType>(field)->getTypeAtIndex(indices[i++]); 
  }
  return indices.slice(0, i); 
}


static void updateDbgIntrinsic(Value* oldV, Value* newV) {
  if (ValueAsMetadata *oldMD = ValueAsMetadata::getIfExists(oldV)) 
    if (MetadataAsValue *oldMDValue = MetadataAsValue::getIfExists(oldV->getContext(), oldMD)) {
      ValueAsMetadata* newMD = ValueAsMetadata::get(newV);
      MetadataAsValue *newMDValue = MetadataAsValue::get(oldV->getContext(), newMD);
      std::vector<DbgInfoIntrinsic *> dbgInstList; 
      for (Use &U : oldMDValue->uses()) {
        if (auto *DVI = dyn_cast<DbgDeclareInst>(U.getUser()))
          dbgInstList.push_back(DVI);
        if (auto *DVI = dyn_cast<DbgValueInst>(U.getUser()))
          dbgInstList.push_back(DVI);
      }
      for (auto I : dbgInstList) 
        CallSite(I).setArgument(0, newMDValue);
    }
  return;
}


//@promotedFields: 
//is used to  record the "field" that the struct nesting is peeled 
//
//for following type { 
// Struct AS{ 
//  //nesting level 5
//  int a; 
// } 
//
// struct BS { 
//   //nesting level 2
//   struct AS xx[N];  //field 0, 
//   struct AS yy;  //field 1
//   struct AS *pp;  //  field 2
// };
//
// struct CS { 
//    //nesting level 1, 
//   struct BS zz[1]; //field 0
// };
//
//
//peelStructType( CS )  
//
//
// will return newType: 
// CS{ 
//   struct BS zz[1]; 
// }
// struct BS{ 
//   struct int xx[N]; 
//   struct int yy; 
//   struct int *pp; 
//}
//@ret new type after peel
Type* SimplifyTypes::peelStructType(Type * T )
{
  if (T->isPointerTy()) { 
    Type *newPT = PointerType::get(
        peelStructType(T->getPointerElementType()), 
        T->getPointerAddressSpace()); 
    //llvm type system is unique 
    return newPT; 
  }

  if (Type *newT = structPeelMap.lookup(T)) { 
    return newT; 
  }


  if (auto *ST = dyn_cast<StructType>(T)) {
    if (ST->isOpaque())
      return Type::getInt8Ty(T->getContext());

    //Peel the struct nesting the one integer field recursivly 
    if (ST->getNumElements() == 1) {
      auto *EltT = ST->getElementType(0);
      //TODO need check TypeAllocSize, to support pack attribute 

      Type *retType = peelStructType(EltT);
      //TODO, should we only peel struct for "float" and "integer" type
      if (retType->isIntegerTy() || retType->isFloatingPointTy() || retType->isPointerTy()) { 
        structPeelMap[T] = retType; 
      }
      else { 
        if (retType != EltT) { 
          retType =  StructType::create(makeArrayRef(retType), ST->getName()); 
          structPeelMap[T] = retType; 
        }
        else { 
          retType = T; 
        }
      }
      return retType; 
    }
    else { 
      bool changed = false; 
      SmallVector<Type*, 4> newEltTypes;
      for (auto *ET : ST->elements()) { 
        auto *newElt = peelStructType(ET ); 
        if (newElt != ET) { 
          changed = true; 
        }
        newEltTypes.push_back(newElt); 
      }

      if (changed) {
        auto *newST = StructType::create(newEltTypes, ST->getName()); 
        structPeelMap[T] = newST; 
        return newST; 
      }
      else 
        return ST; 
    }
  }
  else if (auto *ST = dyn_cast<SequentialType>(T)) { 
    auto *EltT = ST->getElementType();
    /* for "
     * struct S1{ 
     *    int a[1]; 
     * }
     */   
    auto *newEltT = peelStructType(EltT); 
    if (newEltT != EltT) { 
      if (isa<ArrayType>(T)) { 
        ArrayType* newArrayType = ArrayType::get(newEltT, cast<ArrayType>(ST)->getNumElements()); 
        structPeelMap[T] = newArrayType; 
        return newArrayType ;
      }
      else if (isa<VectorType>(T)) { 
        VectorType *newVectorType = VectorType::get(newEltT, cast<VectorType>(ST)->getNumElements());
        structPeelMap[T] = newVectorType; 
        return newVectorType; 
      }
    }
    else { 
      return ST; 
    }
  }
  return  T; 
}

Constant* SimplifyTypes::simplifyConstant(Constant *cv)
{
  Type *oldType = cv->getType(); 

  if (!structPeelMap.count(oldType)) { 
    return cv; 
  }


  Type *newType = structPeelMap[oldType]; 

  if (isa<ConstantAggregateZero>(cv)) { 
    if (isa<CompositeType>(newType)){ 
      return ConstantAggregateZero::get(newType); 
    }
    else if (newType->isPointerTy()){ 
      return ConstantPointerNull::get(cast<PointerType>(newType)); 
    }
    else if(newType->isIntegerTy()){ 
      return ConstantInt::get(newType, 0); 
    }
    else if(newType->isFloatingPointTy()){ 
      return ConstantFP::get(newType, 0); 
    }
    else { 
      llvm_unreachable("unexpected, peelStructType only generate struct/pointer/integer/floatingpoint"); 
      return nullptr; 
    }
  }
  
  if (isa<StructType>(oldType)) { 
    assert(isa<ConstantStruct>(cv) && "Unexpected"); 
    ConstantStruct *scv = cast<ConstantStruct>(cv); 

    if (!isa<StructType>(newType)) { 
      Constant *fieldcv = scv->getAggregateElement(0u); 
      while(isa<ConstantStruct>(fieldcv)) { 
        fieldcv = cast<ConstantStruct>(fieldcv)->getAggregateElement(0u); 
      }
      assert(fieldcv->getType() == newType && "unexpected"); 
      return fieldcv; 
    }

    //if it is oldType is struct type with peeling off inner nest 
    SmallVector<Constant*, 4> values; 

    for( unsigned i = 0; i < cast<StructType>(oldType)->getNumElements(); i++) { 
      Constant *fieldConst = scv->getAggregateElement( i ); 
      Constant *newFieldConst = simplifyConstant(fieldConst); 
      values.push_back(newFieldConst); 
    }
    return ConstantStruct::get(cast<StructType>(newType), values); 
  }
  else if (isa<ArrayType>(oldType)) { 
    assert(isa<ArrayType>(oldType) && "Unexpeced"); 


    //if it is oldType is ArrayType with peeling off inner nest 
    SmallVector<Constant*, 4> values; 

    ConstantArray *acv = cast<ConstantArray>(cv); 
    for( unsigned i = 0; i < cast<ArrayType>(oldType)->getNumElements();  i++) { 
      Constant *fieldConst = acv->getAggregateElement( i ); 
      Constant *newFieldConst = simplifyConstant(fieldConst); 
      values.push_back(newFieldConst); 
    }
    return ConstantArray::get(cast<ArrayType>(newType), values); 
  }
  llvm_unreachable("unexpect aggregate type"); 
  return nullptr;
}

Value* SimplifyTypes::simplifyGEP(GEPOperator* gep, Value* newBase ) { 
  Value *base = gep->getPointerOperand(); 

  Type *curOldType = base->getType()->getPointerElementType(); 
  Type *curNewType = newBase->getType()->getPointerElementType(); 
  SmallVector<Value*, 4> idxs; 

  idxs.push_back(gep->getOperand(1)); 

  for( unsigned i = 2; i < gep->getNumOperands(); i++) { 
    Value *idxV = gep->getOperand(i); 
    if (curNewType->isArrayTy()) { 
      curOldType = curOldType->getArrayElementType(); 
      curNewType = curNewType->getArrayElementType(); 
      idxs.push_back(idxV); 
    }
    else if (curNewType->isVectorTy()) { 
      curOldType = curOldType->getVectorElementType(); 
      curNewType = curNewType->getVectorElementType(); 
      idxs.push_back(idxV); 
    }
    else if (curNewType->isStructTy()) { 
      assert( isa<ConstantInt>(idxV) && "unexpected"); 
      int idx = cast<ConstantInt>(idxV)->getValue().getSExtValue(); 
      curOldType = curOldType->getStructElementType(idx); 
      curNewType = curNewType->getStructElementType(idx); 
      idxs.push_back(idxV); 
    }
    else { 
      break; 
    }
  }
  assert(curNewType == peelStructType(curOldType) && "unexpected"); 
  IRBuilder<> builder(CurM->getContext()); 

  Value *ret = nullptr; 

  if (isa<GetElementPtrInst>(gep)) { 
    auto * gep_inst = cast<GetElementPtrInst>(gep); 
    builder.SetInsertPoint(gep_inst); 

    Value *new_gep_inst = nullptr; 
    if (idxs.size() == 1 && isa<ConstantInt>(idxs[0]) && cast<ConstantInt>(idxs[0])->getValue().getZExtValue() == 0) { 
      new_gep_inst = newBase; 
    }
    else { 
      new_gep_inst = builder.CreateGEP(newBase, idxs, gep->getName()); 
    }

    ret = castType(new_gep_inst, gep->getType(), builder); 

  }
  else if (isa<ConstantExpr>(gep)) { 

    Value *new_gep_const = nullptr; 
    if (idxs.size() == 1 && isa<ConstantInt>(idxs[0]) && cast<ConstantInt>(idxs[0])->getValue().getZExtValue() == 0) { 
      new_gep_const = newBase; 
    }
    else { 
      new_gep_const =  builder.CreateGEP(newBase, idxs, gep->getName()); 
    }

    ret = castType(new_gep_const, gep->getType(), builder); 

  }
  if (!ret) { 
    dbgs() << "------------------- Error ------------------\n"; 
    gep->dump(); 
    newBase->dump(); 
    assert(ret && "unexpected, simplifyGEP failed \n"); 
  }
  DEBUG(
      llvm::dbgs() << "simplify GEP from :\n"; 
      gep->dump(); 
      llvm::dbgs() << "to:\n"; 
      ret->dump(); 
      ); 
  return ret; 
}

bool SimplifyTypes::isCast1(Value* v) 
{
  if (!isa<CallInst>(v)) { 
    return false; 
  }

  CallInst *call = cast<CallInst>(v); 
  Function *func = call->getCalledFunction(); 
  if (func->getName().slice(0,5).equals("cast1") && func->isDeclaration()){ 
    return true; 
  }
  return false; 
}

bool SimplifyTypes::isCast2(Value* v) 
{
  if (!isa<CallInst>(v)) { 
    return false; 
  }

  CallInst *call = cast<CallInst>(v); 
  Function *func = call->getCalledFunction(); 
  if (func->getName().slice(0, 5).equals("cast2") && func->isDeclaration()){ 
    return true; 
  }
  return false; 
}

bool SimplifyTypes::populateBitCastDown(BitCastOperator* bitcast)
{

  DEBUG(
      llvm::dbgs() << "___> populate down bitcast: \n"; 
      bitcast->dump(); 
      ); 

  SmallVector<User*, 4> users; 
  for( auto &use: bitcast->uses()) { 
    User *user = use.getUser(); 
    users.push_back(user); 
  }
  for( auto *user: users) { 
    DEBUG(
        llvm::dbgs() <<"handle user of the bitcast:\n"; 
        user->dump(); 
      );
    if (isa<GEPOperator>(user)) { 
      GEPOperator *gep = cast<GEPOperator>(user);
      Value *ret = simplifyGEP(gep, bitcast->getOperand(0)); 
      gep->replaceAllUsesWith(ret); 
      if (isa<Instruction>(gep)) { 
        cast<Instruction>(gep)->eraseFromParent(); 
      }
    }
    else if (isa<BitCastOperator>(user)) { 
      //the possible scenario
      BitCastOperator *bitcast_next = cast<BitCastOperator>(user); 
      if (bitcast_next->getDestTy() == bitcast->getSrcTy()) { 

        DEBUG(
            llvm::dbgs() << "bitcast+bitcast and eliminate both\n"; 
            ); 
        bitcast_next->replaceAllUsesWith(bitcast->getOperand(0)); 
      }
      else { 
        IRBuilder< > builder(user->getContext()); 
        if (isa<Instruction>(bitcast_next)) { 
          builder.SetInsertPoint(cast<Instruction>(bitcast_next)); 
        }
        Value *new_bitCast = builder.CreateBitCast(bitcast->getOperand(0), bitcast_next->getDestTy()); 

        bitcast_next->replaceAllUsesWith(new_bitCast); 

        DEBUG(
            llvm::dbgs() << "bitcast+bitcast, generate one new bitcast: \n"; 
            new_bitCast->dump(); 
            ); 
      }
      if (isa<Instruction>(bitcast_next)) { 
        cast<Instruction>(bitcast_next)->eraseFromParent(); 
      }
    }
    else if (isa<LoadInst>(user)) { 
      Value* addr = bitcast->getOperand(0); 
      IRBuilder<> builder(cast<LoadInst>(user)); 
      LoadInst *oldLoad = cast<LoadInst>(user); 
      LoadInst *newLoad = builder.CreateLoad(addr, user->getName()) ; 
      Value *cast = castType(newLoad, oldLoad->getType(), builder); 
      oldLoad->replaceAllUsesWith(cast); 
      DEBUG(
          llvm::dbgs()<< "generate new load replace old load \n"; 
          newLoad->dump(); 
          );
      oldLoad->eraseFromParent(); 
    }
    else if (isa<StoreInst>(user)) { 
      StoreInst *store = cast<StoreInst>(user); 
      IRBuilder<> builder(store); 
      assert(bitcast->getSrcTy()->isPointerTy() && "unexpected, bitcast to store's pointer operand should be pointer"); 

      Value *addr = store->getPointerOperand(); 
      Value *data = store->getValueOperand(); 
      StoreInst* newStore = nullptr; 
      if (addr == bitcast) { 
        Type *newType = peelStructType(data->getType()); 
        Value *newData = castType(data, newType, builder); 
        newStore = builder.CreateStore(newData, bitcast->getOperand(0)); 
      }
      else { 
        Value *newAddr = castType(
            addr, 
            peelStructType(addr->getType()), 
            builder); 
        newStore = builder.CreateStore( bitcast->getOperand(0), newAddr); 
      }
      newStore->setAlignment(store->getAlignment()); 
      DEBUG(
          llvm::dbgs() << "generate new Store replace old Store:\n"; 
          newStore->dump(); 
          ); 
      store->eraseFromParent(); 
    }
    else if (isa<PHINode>(user)) { 
      //TODO, set insert pointer after PHINode
      PHINode *phi = cast<PHINode>(user); 
      IRBuilder<> builder(phi); 
      builder.SetInsertPoint(phi) ; 
      PHINode *newPhi = builder.CreatePHI(bitcast->getSrcTy(), phi->getNumIncomingValues(),phi->getName()); 
      for(int i = 0; i < phi->getNumIncomingValues(); i++){ 
        Value *v = phi->getIncomingValue(i); 
        assert(v->getType()->isPointerTy() && "unexpected"); 
        if (v == bitcast) { 
          v = (bitcast->getOperand(0)); 
        }
        else { 
          if (isa<Instruction>(v)) { 
            builder.SetInsertPoint(cast<Instruction>(v)->getNextNode()); 
          }
          v = castType(v, bitcast->getSrcTy(), builder); 
        }
        BasicBlock* bb = phi->getIncomingBlock(i); 
        newPhi->addIncoming(v, bb); 
      }
      BasicBlock *bb = phi->getParent(); 
      builder.SetInsertPoint(bb->getFirstNonPHI()); 
      Value *cast = castType(newPhi, phi->getType(), builder); 

      phi->replaceAllUsesWith(cast); 
      phi->eraseFromParent(); 
    }
    else if (isa<SelectInst>(user)) { 
      SelectInst *select = cast<SelectInst>(user); 
      IRBuilder<> builder(select); 
      Value *tv = select->getTrueValue(); 
      Value *fv = select->getFalseValue(); 
      if (bitcast == tv){ 
        tv = bitcast->getOperand(0);
        fv = castType(fv, bitcast->getSrcTy(), builder);
      }
      else if (bitcast == fv) { 
        fv = (bitcast->getOperand(0)); 
        tv = castType(tv, bitcast->getSrcTy(), builder); 
      }
      else { 
        llvm_unreachable("unexpected, the selectInst's condition is bitcast"); 
      }
      Value* new_select = builder.CreateSelect(select->getCondition(), tv, fv); 
      Value *bitcast_new_select = castType(new_select, select->getType(), builder); 
      select->replaceAllUsesWith(bitcast_new_select); 
      select->eraseFromParent(); 
    }
    else if (isa<ICmpInst>(user)) { 
      ICmpInst *cmp = cast<ICmpInst>(user); 
      IRBuilder<> builder(cmp); 
      for( int i = 0;  i < cmp->getNumOperands(); i++) { 
        Value *v = cmp->getOperand(i) ;
        if (v== bitcast){ 
          cmp->setOperand(i, bitcast->getOperand(0)); 
        }
        else { 
          Value *newV = castType(v, bitcast->getSrcTy(), builder); 
          cmp->setOperand(i, newV); 
        }
      }
    }
    else if (isa<PragmaInst>(user)) { 
      user->replaceUsesOfWith(bitcast, bitcast->getOperand(0));
    }
    else if (isa<IntrinsicInst>(user)) { 

      IntrinsicInst *intrinsic_call = cast<IntrinsicInst>(user); 
      IRBuilder<> builder(intrinsic_call); 
      Intrinsic::ID intrinsic_id = intrinsic_call->getIntrinsicID(); 

      SmallVector<Intrinsic::IITDescriptor, 4> descs; 
      getIntrinsicInfoTableEntries(intrinsic_id, descs); 
      ArrayRef<Intrinsic::IITDescriptor> infos(descs); 
  
      SmallVector<Type*, 4> overloadArgTypes; 
      SmallVector<Value*,4> argValues; 
      for( Value *arg : intrinsic_call->arg_operands()) { 
        if (arg == bitcast) { 
          arg = bitcast->getOperand(0); 
          argValues.push_back(arg); 
          continue; 
        }
        Type* newArgType = peelStructType(arg->getType()); 
        if (newArgType != arg->getType()) { 
          arg = castType(arg, newArgType, builder); 
        }
        argValues.push_back(arg); 
      }

      Type *newRetType = peelStructType(intrinsic_call->getType()); 

      FunctionType *  IFTy = intrinsic_call->getCalledFunction()->getFunctionType(); 
      bool IsVarArg = IFTy->isVarArg();

      bool matchReturnType = !Intrinsic::matchIntrinsicType(newRetType, infos, overloadArgTypes); 
      assert( matchReturnType && "unexpected, the intrinsic return type mismatch"); 


      for( int i = 0; i < IFTy->getNumParams(); i++) { 
        bool matchArgType = !Intrinsic::matchIntrinsicType(argValues[i]->getType(), infos, overloadArgTypes); 
        assert(matchArgType && "unexpected, "); 
      }

      if (IsVarArg) { 
        bool matchArgType = !Intrinsic::matchIntrinsicVarArg(IsVarArg, infos); 
        assert(matchArgType && "unexpected, "); 
      }


      auto *F =
        Intrinsic::getDeclaration(CurM, intrinsic_id, overloadArgTypes); 

      CallInst *newCall = builder.CreateCall(F, argValues); 
    
      Value *ret = newCall; 
      if (newRetType != intrinsic_call->getType()){ 
        ret = castType(newCall, intrinsic_call->getType(), builder); 
      }
  
  
      Function *old_intrinsic_func = intrinsic_call->getCalledFunction(); 
      intrinsic_call->replaceAllUsesWith(ret); 
      intrinsic_call->eraseFromParent(); 
      DEBUG(
          llvm::dbgs() << "delete old call, and generate new IntrinsicCal: \n"; 
          newCall->dump(); 
          ); 
      
      if (F != old_intrinsic_func){ 
        updateIntrinsicSet.insert(intrinsic_call->getCalledFunction()); 
      }
    }
    else { 
      bitcast->dump(); 
      user->dump(); 
      llvm_unreachable("there is unexpected user of bitcast"); 
    }
  }

  if (bitcast->getNumUses())
    return false; 
  else
    return true; 
}


bool SimplifyTypes::populateBitCastUp(BitCastOperator *bitcast) 
{
  //populate to up


  Value *srcV = bitcast->getOperand(0); 
  DEBUG(
      llvm::dbgs() << "___> populate up bitcast: \n"; 
      bitcast->dump(); 
      llvm::dbgs() << "handle source from:\n"; 
      srcV->dump(); 
      ); 
  if (isa<CallInst>(srcV)) { 
    assert(isCast2(srcV) && "Unexpected"); 
    CallInst *cast2 = cast<CallInst>(srcV); 
    IRBuilder<> builder(cast2); 
    Value* newSrcV = cast2->getArgOperand(0); 
    assert(newSrcV->getType() == bitcast->getDestTy() && "Unexpected"); 
    bitcast->replaceAllUsesWith(newSrcV); 
  }
  else if (isa<BitCastOperator>(srcV)) { 
    BitCastOperator *up_bitcast = cast<BitCastOperator>(srcV); 
    IRBuilder<> builder(CurM->getContext()); 
    if (isa<Instruction>(up_bitcast)) { 
      builder.SetInsertPoint(cast<Instruction>(up_bitcast)); 
    }
    Value *ret = nullptr; 
    if (peelStructType(up_bitcast->getSrcTy()) != bitcast->getDestTy()){ 
      ret = builder.CreateBitCast(up_bitcast->getOperand(0), bitcast->getDestTy()); 
    }
    else { 
      ret = castType(up_bitcast->getOperand(0), bitcast->getDestTy(), builder); 
    }
    bitcast->replaceAllUsesWith(ret); 
  }
  else if (isa<GEPOperator>(srcV)) { 
    GEPOperator *gep = cast<GEPOperator>(srcV); 
    IRBuilder<> builder(CurM->getContext()); 
    if (isa<Instruction>(gep)) { 
      builder.SetInsertPoint(cast<Instruction>(gep)); 
    }
    Value *addr = gep->getPointerOperand(); 
    Value *newAddr = castType(addr, peelStructType(addr->getType()), builder); 
    Value *newGEP = simplifyGEP( gep, newAddr) ; 
    bitcast->replaceAllUsesWith(newGEP); 
  }
  else if (isa<ExtractValueInst>(srcV)) { 
    ExtractValueInst  *extract = cast<ExtractValueInst>(srcV); 
    IRBuilder<> builder(extract); 
    Value *agg = extract->getAggregateOperand(); 
    Type *newAggType = peelStructType(agg->getType()); 
    if (isa<CompositeType>(newAggType)) { 
      Value *newAgg = castType(agg, newAggType, builder); 
      ArrayRef<unsigned> indices = castIndices(extract->getIndices(), newAgg->getType(), bitcast->getDestTy()); 
      Value *newExtract = builder.CreateExtractValue(newAgg, indices); 
      bitcast->replaceAllUsesWith(newExtract); 
    }
    else { 
      assert( newAggType->isPointerTy() && "Unexpected"); 
      Value * ret = castType(agg, bitcast->getType(), builder); 
      bitcast->replaceAllUsesWith(ret); 
    }
  }
  else if (isa<LoadInst>(srcV)) { 
    LoadInst * load = cast<LoadInst>(srcV); 
    IRBuilder<> builder(load); 
    Value *addr = load->getPointerOperand(); 
    Value *newAddr = castType( addr, peelStructType(addr->getType()), builder); 
    Value *newLoad = builder.CreateLoad(newAddr); 
    bitcast->replaceAllUsesWith(newLoad); 
  }
  else { 
    srcV->dump(); 
    bitcast->dump(); 
    llvm_unreachable("there is unexpected user of bitcast"); 

    return false; 
  }
  return true; 
}

bool SimplifyTypes::populateCast1Up( CallInst* cast1) 
{
  Value* srcV = cast1->getArgOperand(0); 
  DEBUG(
      llvm::dbgs() << "___> populate up cast1: \n"; 
      cast1->dump(); 
      llvm::dbgs() << "handle source from:\n"; 
      srcV->dump(); 
      ); 

  if (isa<CallInst>(srcV)) { 
    assert(isCast2(srcV) && "unexpected"); 
    CallInst *cast2 = cast<CallInst>(srcV); 
    Value *src2 = cast2->getArgOperand(0); 
    cast1->replaceAllUsesWith(src2); 
  }
  else if (isa<InsertValueInst>(srcV)) { 
    InsertValueInst *insert = cast<InsertValueInst>(srcV); 
    IRBuilder<> builder(insert); 
    Value *agg = insert->getAggregateOperand(); 
    Value *fieldV = insert->getInsertedValueOperand(); 
    if (isa<CompositeType>(cast1->getType())) { 
      Value *newFieldV = castType(fieldV, peelStructType(fieldV->getType()), builder); 
      Value *newAgg = castType(agg, cast1->getType(), builder); 
      IRBuilder<> builder(insert); 
      ArrayRef<unsigned>  indices = castIndices(insert->getIndices(), newAgg->getType(), newFieldV->getType()); 
      Value *newInsert = builder.CreateInsertValue(newAgg, newFieldV, indices); 
      cast1->replaceAllUsesWith(newInsert); 
    }
    else { 
      Value *newCast = castType( fieldV, cast1->getType(), builder); 
      cast1->replaceAllUsesWith(newCast); 
    }
  }
  else if (isa<ExtractValueInst>(srcV)) { 
    ExtractValueInst *extract = cast<ExtractValueInst>(srcV); 
    Value *agg = extract->getAggregateOperand(); 
    Type* newAggType = peelStructType(agg->getType());
    ArrayRef<unsigned> indices = castIndices( extract->getIndices(), newAggType, cast1->getType()); 
    IRBuilder<> builder(extract); 
    Value *ret = nullptr; 
    if( indices.size()) { 
      Value *newAgg = castType(agg, newAggType, builder); 
      ret = builder.CreateExtractValue(newAgg, indices); 
    }
    else { 
      ret = castType(agg, cast1->getType(), builder); 
    }

    cast1->replaceAllUsesWith(ret); 
  }
  else if (isa<PHINode>(srcV)) { 
    PHINode *phi = cast<PHINode>(srcV); 
    IRBuilder<> builder(phi); 
    builder.SetInsertPoint(phi) ; 
    PHINode *newPhi = builder.CreatePHI(cast1->getType(), phi->getNumIncomingValues(), phi->getName()); 

    for(int i = 0; i < phi->getNumIncomingValues(); i++){ 
      Value *v = phi->getIncomingValue(i); 
      Value *cast1 = castType(v, cast1->getType(), builder); 
      if (isCast2(v) ) { 
        v = cast<CallInst>(v)->getArgOperand(0); 
      }
      else { 
        if (isa<Instruction>(v)) { 
          builder.SetInsertPoint(cast<Instruction>(v)->getNextNode()); 
        }
        v = castType(v, cast1->getType(), builder); 
      }
      BasicBlock* bb = phi->getIncomingBlock(i); 
      newPhi->addIncoming(v, bb); 
    }

    cast1->replaceAllUsesWith( newPhi); 
  }
  else if (isa<SelectInst>(srcV)) { 
    SelectInst *select = cast<SelectInst>(srcV); 
    IRBuilder<> builder(select); 
    Value *tv = select->getTrueValue(); 
    Value *fv = select->getFalseValue(); 
    tv = castType(tv, cast1->getType(), builder); 
    fv = castType(fv, cast1->getType(), builder); 

    Value* new_select = builder.CreateSelect(select->getCondition(), tv, fv, select->getName()); 
    cast1->replaceAllUsesWith(new_select); 
  }
  else if (isa<LoadInst>(srcV)){ 
    LoadInst *load = cast<LoadInst>(srcV); 
    IRBuilder<> builder(load); 
    Value *addr = load->getOperand(0); 
    Type *simplifiedType = peelStructType(addr->getType()); 
    Value *newAddr = castType(addr, simplifiedType, builder); 
    LoadInst *newLoad = builder.CreateLoad( newAddr, load->isVolatile(), load->getName()); 
    cast1->replaceAllUsesWith(newLoad); 
  }
  else {
    srcV->dump(); 
    cast1->dump(); 
    llvm_unreachable("TODO , handle cast1"); 

  }
  return true; 
}

bool SimplifyTypes::populateCast2Down(CallInst * cast2) 
{
  if (!cast2->getNumUses()) { 
    //delete usless
    RecursivelyDeleteTriviallyDeadInstructions(cast2, TLI); 
    return true; 
  }
  DEBUG( 
      llvm::dbgs() << "___> populate down cast2: \n" ; 
      cast2->dump(); 
      ); 

  IRBuilder<> builder(cast2); 

  SmallVector<User*, 4> users; 
  for( auto &use: cast2->uses()) { 
    User *user = use.getUser(); 
    users.push_back(user); 
  }
  for ( int i  = 0; i < users.size() ; i++ ) { 
    User *user = users[i]; 
    DEBUG(
        llvm::dbgs() << "handle user: \n"; 
        user->dump(); 
        ); 
    if (isa<CallInst>(user)) { 
      assert( isCast1(user) && "Unexpected"); 
      CallInst *cast1 = cast<CallInst>(user); 
      cast1->replaceAllUsesWith(cast2->getArgOperand(0)); 
      cast1->eraseFromParent(); 
    }
    else if (isa<ExtractValueInst>(user)) { 
      ExtractValueInst * extract = cast<ExtractValueInst>(user); 
      ArrayRef<unsigned> indices = castIndices(extract->getIndices(), 
          cast2->getArgOperand(0)->getType(), 
          extract->getType()); 
      Value *ret = nullptr; 
      if (indices.size() == 0) {
        // delete extract,  cast 
        ret = castType(cast2->getArgOperand(0), extract->getType(), builder); 
      }
      else { 
        ret = builder.CreateExtractValue(cast2->getArgOperand(0), indices, extract->getName()); 
        if (ret->getType() != extract->getType()) { 
          ret = castType(ret, extract->getType(), builder); 
        }
      }
      extract->replaceAllUsesWith(ret); 
      extract->eraseFromParent(); 
    }
    else if (isa<InsertValueInst>(user)) { 
      InsertValueInst * insert = cast<InsertValueInst>(user); 
      llvm_unreachable("TODO, handle the user of cast2"); 
    }
    else if (isa<StoreInst>(user)) { 
      StoreInst * store = cast<StoreInst>(user); 
      Value *addr = store->getPointerOperand(); 
      Type *dstType = peelStructType(addr->getType()); 
      Value *bitcastUp = castType(addr, dstType, builder); 
      Value *srcValue = cast2->getArgOperand(0); 
      builder.CreateStore(srcValue, bitcastUp, store->isVolatile());
      store->eraseFromParent(); 
    }
    else if (isa<PHINode>(user)) { 
      llvm_unreachable("TODO, handle the user of cast2"); 
    }
    else if (isa<SelectInst>(user)) { 
      llvm_unreachable("TODO, handle the user of cast2"); 
    }
    else { 
      cast2->dump(); 
      user->dump(); 

      llvm_unreachable( "TODO, handle user of cast2"); 
    }
  }
  if (cast2->getNumUses()){ 
    return false; 
  }

  return true; 
}

bool SimplifyTypes::runOnModule(Module &M) {

  TLI = &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); 

  CurM = &M; 

  // Collect the functions in another vector,
  // because we will introduce new functions and erase old funtions
  std::vector<Function *> Funcs;
  for (Function &F : M)
    Funcs.push_back(&F);

  // Optimize function arguments
  for (Function *F : Funcs) {
    if (F->isDeclaration() || F->isVarArg())
      continue;
    for( auto &bb: *F) { 
      for (auto &inst: bb) { 
        if (isa<BitCastOperator>(inst)) { 
          BitCastOperator *bitcast = cast<BitCastOperator>(&inst); 
          Type *srcType = bitcast->getSrcTy(); 
          Type *destType = bitcast->getDestTy(); 
          if (isValidTypeCast(srcType, destType)) { 
            bitcastUpWorkList.push_back(bitcast); 
          }
          else if (isValidTypeCast(destType, srcType)) { 
            bitcastDownWorkList.push_back(bitcast); 
          }
          else {
            //THIS is bitcast from user, we can process it  anywe 
          }
        }
      }
    }

    // Although black box function is marksed external to prevent
    // llvm native optimizations, we can still peel off the structures 
    // on its arguments.
#if 0
    if (!F->hasLocalLinkage() && !F->hasFnAttribute("fpga.blackbox"))
      continue;
#endif 

    SmallVector<Type*, 4> newArgTypes; 
    bool changed = false; 
    for( auto &arg : F->args()){ 
      Type *arg_type = arg.getType(); 

      Type *newType = peelStructType(arg_type); 
      if (newType != arg_type) { 
        changed = true;
      }
      newArgTypes.push_back(newType); 
    }
    if (!changed) { 
      continue; 
    }

    // If some argument has "sret" attribute, the function should 
    // have void return type
  
    Type *newRetType = peelStructType(F->getReturnType()); 
    FunctionType *NewFuncTy = 
        FunctionType::get(newRetType, newArgTypes, false);
  
    Function *NewF = 
        Function::Create(NewFuncTy, F->getLinkage(), "", F->getParent());
  
    NewF->setCallingConv(F->getCallingConv());
    NewF->takeName(F);
    NewF->copyMetadata(F, 0); //copy metadata

    LLVMContext &Ctx = F->getContext();
    AttributeList OldAttrs = F->getAttributes();
  
    // Copy Fn Attributs
    NewF->addAttributes(AttributeList::FunctionIndex, OldAttrs.getFnAttributes());
  
    // Copy return attributes if the return type is unchanged
    if (NewF->getReturnType() == F->getReturnType()) 
      NewF->addAttributes(AttributeList::ReturnIndex, OldAttrs.getRetAttributes());
  
    // Move basic blocks from old function to new function
    NewF->getBasicBlockList().splice(NewF->begin(), F->getBasicBlockList());
  
    IRBuilder<> IB(NewF->getContext()); 
    // Replace the arguments
    for (auto It = F->arg_begin(), Ie = F->arg_end(), NIt = NewF->arg_begin();
         It != Ie; ++It, ++NIt) {
      Argument *OldArg = &*It;
      Argument *NewArg = &*NIt;
  
      // Take argument name
      NewArg->takeName(OldArg);
  
      AttributeSet OldAS = OldAttrs.getParamAttributes(OldArg->getArgNo());
     
      if (NewArg->getType() == OldArg->getType()) {
        updateDbgVariableIntrinsics(OldArg, NewArg);
        // Unchanged argument, just replce all its uses with new argument
        OldArg->replaceAllUsesWith(NewArg);
        // Copy the parameter's attributes
        NewF->addParamAttrs(NewArg->getArgNo(), OldAS);
      } else {
        // If the "byval" argument is tranformed into scalar, allocate a temp
        // variable for it.
        IB.SetInsertPoint(&*NewF->getEntryBlock().getFirstInsertionPt());
        DEBUG( 
            llvm::dbgs() << "____>chang arg from: \n" ; 
            OldArg->dump(); 
            llvm::dbgs() << " to:\n"; 
            NewArg->dump(); 
            ); 
        Value *cast = castType(NewArg, OldArg->getType(), IB); 
        OldArg->replaceAllUsesWith(cast); 

        // Only copy the string attributes (HLS specified attributes). 
        // Since the argument type is changed, the llvm's native attributes
        //  become invalid.
        SmallVector<Attribute, 4> NewAttrList;
        for (auto AttrIt = OldAS.begin(), AttrIe = OldAS.end();
             AttrIt != AttrIe; ++AttrIt) {
          if (AttrIt->isStringAttribute()) 
            NewAttrList.push_back(*AttrIt);
        }
  
        NewF->addParamAttrs(NewArg->getArgNo(), 
                            AttributeSet::get(Ctx, NewAttrList));
      }
    }
    if (NewF->getReturnType() != F->getReturnType()) { 
      for( BasicBlock &bb: *NewF){ 
        TerminatorInst *term = bb.getTerminator(); 
        if (isa<ReturnInst>(term)) { 
          ReturnInst * ret = cast<ReturnInst>(term); 
          IRBuilder<> builder(term); 
          Value *retValue = ret->getReturnValue(); 
          retValue = castType(retValue, NewF->getReturnType(), builder); 
          builder.CreateRet(retValue);
          ret->eraseFromParent(); 
        }
      }
    }

    //----------------------- replace callSite --------------
    std::vector<User *> Users(F->user_begin(), F->user_end());
    for (User *U : Users) {
      CallInst *CI = cast<CallInst>(U);
      if (CI->getCalledFunction() != F) { 
        CI->replaceUsesOfWith(F, NewF);
        continue;
      }
      IB.SetInsertPoint(CI);
  
      SmallVector<Value *, 4> NewActualArgs;
      auto AIt = CI->arg_begin();
      for (auto It = F->arg_begin(), Ie = F->arg_end(), NIt = NewF->arg_begin(); 
           It != Ie; ++It, ++AIt, ++NIt) {
        Argument *OldArg = &*It;
        Argument *NewArg = &*NIt;
        Value *OldActArg = *AIt;
        
        DEBUG( 
            if (OldArg->getType() != NewArg->getType()) { 
              llvm::dbgs() << "___> chang call Actual arg from: \n" ; 
              OldActArg->dump(); 
              llvm::dbgs() << " to:\n"; 
              NewArg->getType()->dump(); 
            }); 
        // Skip argument with "sret" atrribute, it will be handled later
        if (NewArg->getType() == OldArg->getType()) { 
          // Unchanged arguments
          NewActualArgs.push_back(OldActArg);
        } 
        else {
          Value *NewActArg = castType(OldActArg, NewArg->getType(), IB); 
          NewActualArgs.push_back(NewActArg); 
        }
      }
  
      CallInst *NewCall = IB.CreateCall(NewF, NewActualArgs);
  
      if (CI->hasName())
        NewCall->takeName(CI);
  
      NewCall->setCallingConv(CI->getCallingConv());
  
      AttributeList OldAttrs = CI->getAttributes();
      AttributeList NewAttrs;
      // Copy function attributes
      NewAttrs = NewAttrs.addAttributes(Ctx, AttributeList::FunctionIndex,
                                        OldAttrs.getFnAttributes());
      // Copy return value attributes 
      NewAttrs = NewAttrs.addAttributes(Ctx, AttributeList::ReturnIndex,
                                          OldAttrs.getRetAttributes());
      
      ///copy argument attributes in call 
      AIt = CI->arg_begin();
      for (auto It = F->arg_begin(), Ie = F->arg_end(), NIt = NewF->arg_begin(); 
           It != Ie; ++It, ++AIt) {
        Argument *OldArg = &*It;
        Argument *NewArg = &*NIt;
        Value *OldActArg = *AIt;

        // Copy the parameter attributes
        AttributeSet OldAS = OldAttrs.getParamAttributes(OldArg->getArgNo());
        AttributeSet NewAS;
        if (NewArg->getType() == OldArg->getType())
          NewAS = OldAS;
        else {
          SmallVector<Attribute, 4> NewAttrList;
          for (auto AttrIt = OldAS.begin(), AttrIe = OldAS.end();
               AttrIt != AttrIe; ++AttrIt) {
            if (AttrIt->isStringAttribute()) 
              NewAttrList.push_back(*AttrIt);
          }
          NewAS = AttributeSet::get(Ctx, NewAttrList);
        }
  
        NewAttrs = NewAttrs.addParamAttributes(Ctx, NewArg->getArgNo(), NewAS);
      }
  
      NewCall->setAttributes(NewAttrs);
  
      // Replace the old call with the new call if it has return value
      Value *callRet = NewCall; 
      if (CI->getType() != NewCall->getType()) { 
        callRet = castType(NewCall, CI->getType(),  IB); 
      }
      CI->replaceAllUsesWith(callRet);
  
      CI->eraseFromParent();
    }
    F->eraseFromParent(); 
  }

  for( auto &F : M.functions()) {
    //--------------  handle local data with Simiplified Type ------------------
    //-- TODO, handle trivial case: bitcast(peeledType) to "oldType" 
    //
    if (F.isDeclaration() || F.isVarArg())
      continue;

    // Although black box function is marksed external to prevent
    // llvm native optimizations, we can still peel off the structures 
    // on its arguments.
#if 0
    if (F.hasLocalLinkage() && F.hasFnAttribute("fpga.blackbox"))
      continue;
#endif 

    //
    // Check alloca and bitcast instructions.
    SmallVector<AllocaInst*, 4> allocas; 
    for (auto &I : instructions(F)) {
      if (auto *AI = dyn_cast<AllocaInst>(&I)){ 
        allocas.push_back(AI); 
      }
    }
    for( auto *AI : allocas) { 
      auto *T = AI->getAllocatedType();
      auto *NewT = peelStructType(T); 
      if (T == NewT)
        continue;

      IRBuilder<> builder(AI);
      auto *NewAI = builder.CreateAlloca(NewT, nullptr, AI->getName());
      DEBUG( 
          llvm::dbgs() << "___> change Alloca from: \n"; 
          AI->dump(); 
          llvm::dbgs() << "to: \n"; 
          NewAI->dump(); 
          ); 
      NewAI->copyMetadata(*AI);
      updateDbgIntrinsic(AI, NewAI);
      Value *bitcast = castType(NewAI, AI->getType(), builder); 
      AI->replaceNonMetadataUsesWith(bitcast); 
      //bitcastWorkList.push_back(bitcast); 
      AI->eraseFromParent(); 
    }
  }

  //--------------- handle global  data with Simplified Type -----------------
  SmallVector<GlobalVariable*, 4> obsoletedGvs; 
  SmallVector<GlobalVariable*, 4> globals; 

  for(auto &gv : M.globals()) { 
    globals.push_back(&gv); 
  }
  for( auto *gv: globals) { 
    auto *NewType = peelStructType(gv->getType()); 
    if (NewType == gv->getType()) { 
      continue; 
    }


    Constant *initializer = gv->getInitializer(); 
    Constant *newInitializer = simplifyConstant(initializer); 

    auto newGV = new GlobalVariable(M, NewType->getPointerElementType(), gv->isConstant(),
                 gv->getLinkage(),   newInitializer,
                 gv->getName(), gv, 
                 gv->getThreadLocalMode(),  gv->getType()->getPointerAddressSpace()); 

    DEBUG(
        llvm::dbgs() << " ___> change global Variable from : \n"; 
        gv->dump(); 
        llvm::dbgs() << "to : \n"; 
        newGV->dump(); 
        ); 
        
    updateDbgIntrinsic(gv, newGV);
    IRBuilder<> builder(M.getContext()); 
    Value *bitcast = castType(newGV, gv->getType(), builder); 

    gv->replaceAllUsesWith(bitcast); 
    DEBUG(
        llvm::dbgs() << "insert bitcast expr\n"; 
        bitcast->dump(); 
        ); 
    obsoletedGvs.push_back(gv); 
  }
  for( int i = 0; i < obsoletedGvs.size(); i++) { 
    obsoletedGvs[i]->eraseFromParent(); 
  }


  DEBUG(llvm::dbgs() << "---- finish build initial bitcastWorkList ----- \n"); 

  //---do instcombine for {bitcast, cast1, cast2 } ----- 


  while (!bitcastDownWorkList.empty() ||
      !bitcastUpWorkList.empty() ||
      !cast1WorkList.empty() ||
      !cast2WorkList.empty() 
      ) { 

    while( !bitcastDownWorkList.empty()){ 
      Value *v = bitcastDownWorkList.front(); 
      bitcastDownWorkList.pop_front(); 
      if (!v) { 
        continue; 
      }

      BitCastOperator *bitcast = cast<BitCastOperator>(v); 
      if (!isValidTypeCast(bitcast->getDestTy(), bitcast->getSrcTy() )) { 
        //if it is not valid ,  it must be because "GEPConst + bitcast" is folded as "bitcast" 
        //for this scenario, we expect  "populateBitCastUp" to handle it 
        continue; 
      }
      populateBitCastDown(bitcast); 
      //populate down the bitcast SrcType
      if (isa<Instruction>(bitcast)) { 
        DEBUG(llvm::dbgs() << "bitcast get erased \n"; ); 
        cast<Instruction>(bitcast)->eraseFromParent(); 
      }
    }
  
    //populate up the "bitcast" destType 
    while(!bitcastUpWorkList.empty()) { 
      Value* v = bitcastUpWorkList.back(); 
      bitcastUpWorkList.pop_back(); 
      if (!v)
        continue; 

      //some constFolder will fold bitcast "ap_int<1>* null" + bitcast( ap_int<1>* , i1*) => "i1 *null"
      if (!isa<BitCastOperator>(v)) { 
        continue; 
      }
      BitCastOperator* bitcast = cast<BitCastOperator>(v); 

      if (!isValidTypeCast(bitcast->getDestTy(), bitcast->getSrcTy() )) { 
        //if it is not valid ,  it must be because "GEPConst + bitcast" is folded as "bitcast" 
        //for this scenario, we expect  "populateBitCastUp" to handle it 
        continue; 
      }

      if (bitcast->getNumUses() == 0) { 
        RecursivelyDeleteTriviallyDeadInstructions(bitcast, TLI); 
        continue; 
      }
      populateBitCastUp(bitcast); 
      if (isa<Instruction>(bitcast)) { 
        DEBUG(llvm::dbgs() << "bitcast get erased \n"); 
        cast<Instruction>(bitcast)->eraseFromParent(); 
      }
    }
  
    //populate up the value of peeled result type 
    while(!cast1WorkList.empty()) { 
      Value *v = cast1WorkList.back();
      cast1WorkList.pop_back(); 
      if (!v)
        continue; 
  
      CallInst *cast1 = cast<CallInst>(v); 
      populateCast1Up( cast1 ); 
      RecursivelyDeleteTriviallyDeadInstructions(cast1, TLI); 
    }
  
    //populate down the Value  of peeled result type 
  
    while (!cast2WorkList.empty()) {
      Value *v = cast2WorkList.back(); 
      cast2WorkList.pop_back(); 
      if (!v) 
        continue; 

      CallInst *cast2 = cast<CallInst>(v); 
      populateCast2Down(cast2); 
      cast2->eraseFromParent(); 
    }
  }

  for( auto &kv: structCast1Map) { 
    Function* cast = kv.second; 
    DEBUG(
      dbgs() << "delete temp cast function\n"; 
      cast->dump(); 
      dbgs() << " all  calls: \n"; 
      for(User *user: cast->users()) { 
        user->dump(); 
      }
      if (cast->getNumUses()) { 
        M.dump(); 
      }
    ); 
  
    assert(!cast->getNumUses() && "unexpected"); 
    cast->eraseFromParent(); 
  }


  for( auto &kv: structCast2Map) { 
    Function* cast = kv.second; 
    DEBUG(
      dbgs() << "delete temp cast function\n"; 
      cast->dump(); 
      dbgs() << " all  calls: \n"; 
      for(User *user: cast->users()) { 
        user->dump(); 
      }
      if (cast->getNumUses()) { 
        M.dump(); 
      }
    ); 
  
    assert(!cast->getNumUses() && "unexpected"); 
    cast->eraseFromParent(); 
  }
  for( auto * intrinsic_func: updateIntrinsicSet) { 
    intrinsic_func->eraseFromParent(); 
  }

  if (structPeelMap.size()) { 
    return true; 
  }
  else 
    return false; 
}

INITIALIZE_PASS(SimplifyTypes, DEBUG_TYPE,
                "Simplify types in the LLVM IR, eliminate redundant type hierarchy",
                false, false)

Pass *llvm::createSimplifyTypesPass(bool PointerInStructOnly) { 
  return new SimplifyTypes(PointerInStructOnly); 
}
